%%%-------------------------------------------------------------------
%%% @author wukai
%%% @copyright (C) 2017, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 07. 十一月 2017 11:58
%%%-------------------------------------------------------------------

-module(frame_pub).
-author("wukai").
-include("../include/mqtt_protocol.hrl").
%% API
-export([do_command/3]).
do_command(Ref, State, Frame) ->
  Flag = Frame#mqtt_frame.frame_flag,
  <<_:4, Dup:1, Qos:2, Retain:1>> = <<Flag>>,
  lager:log(info, self(), "Dup:~p , Qos:~p ,Retain ~p", [Dup, Qos, Retain]),
  {Save, [Top, Len, C]} = case msginfo(Qos, Frame) of
                            [Topic, TLen, Content] ->
                              verify_topic(Topic),
                              ack(0, Ref, State, 0),
                              {handle_retain(Retain, byte_size(Content)),[Topic, TLen, Content]};
                            [Topic, TLen, Content, Id] ->
                              log:log("msgid:~p",[Id]),
                              verify_topic(Topic),
                              ack(Qos, Ref, State, Id),
                              {handle_retain(Retain, byte_size(Content)), [Topic, TLen, Content]};
                            _ -> mqtt_process:exit_self()
                          end,
  P = publish_frame:wrap(State#state.client, Top, Len, C, Dup, Qos, Retain),
  log:log("publish:~p", [P]),
  case Save of
    save -> retain_server:retain_save(State#state.client, Top, P);
    clean -> retain_server:retain_clean(Top);
    _ -> log:log("no handle retain optional") end,
  dispatch_server:send_dispatch(P), State.

msginfo(Qos, Frame) when Qos == 2 orelse Qos == 1 ->
  <<TLen:16, Topic:TLen/binary, Mid:16, Con/binary>> = Frame#mqtt_frame.left_bytes,
  [Topic, TLen, Con, Mid];

msginfo(Qos, Frame) when Qos == 0 ->
  <<TLen:16, Topic:TLen/binary, Con/binary>> = Frame#mqtt_frame.left_bytes,
  [Topic, TLen, Con];
msginfo(_, _) -> exit.

publish_ack(_, State, MSGID) ->
  Bin = <<?FRAME_PUB_ACK:4, 0:4, 2:8, MSGID:16>>,
  mqtt_process:send_binary(State, Bin).

publish_rec(_, State, MSGID) ->
  Bin = <<?FRAME_PUB_REC:4, 0:4, 2:8, MSGID:16>>,
  mqtt_process:send_binary(State, Bin).

verify_topic(Topic) ->
  case okmqtt_topic:topic_check(pub, Topic, pub) of
    true -> ok;
    false ->
      mqtt_process:exit_self(),
      log:log("publish topic error ~p", [Topic])
  end.

handle_retain(Retain, Size) ->
  if
    Retain == 1, Size > 0 -> save;
  %%如果保留标志位=1 消息字节数为0 那么清空此主题下的任何消息,并且将此消息发送给匹配的客户端
    Retain == 1, Size == 0 -> clean;
    true -> ok
  end.

ack(1, Ref, State, Msgid) -> publish_ack(Ref, State, Msgid);
ack(2, Ref, State, Msgid) -> publish_rec(Ref, State, Msgid);
ack(0, _, _, _) -> ok.